export enum TokenType {
	EOF,
	TEXT,
	TEXT_BLOCK,
	COMMAND,
	COMMAND_PARAMETERS,
	BLOCK_BREAK,
}

export class Token {
	constructor(type: TokenType, content: string) {
		this.type = type;
		this.content = content;
	}

	type: TokenType;
	content: string;
}

export class Parser {
	constructor(str: string) {
		this.content = str;
		this.cursor = 0;
	}

	public input(str: string) {
		this.content = str;
		this.cursor = 0;
	}

	public emit(): Token {
		if (this.cursor == this.content.length) {
			return new Token(TokenType.EOF, '');
		} else {
			if (this.content[this.cursor] == '\n') {
				if (this.content[this.cursor + 1] == '\n') {
					this.cursor += 2;
					return new Token(TokenType.BLOCK_BREAK, '\n\n');
				} else {
					let left = this.cursor;
					while (!(this.content[this.cursor] == '@' || this.cursor == this.content.length)) {
						this.cursor++;
					}
					return new Token(TokenType.TEXT, this.content.slice(left, this.cursor));
				}
			} else if (this.content[this.cursor] == '@') {
				++this.cursor;
				let left = this.cursor;
				while (Parser.isAlphaNumeric(this.content[this.cursor])) {
					++this.cursor;
				}
				let right = this.cursor;
				while (Parser.isWhiteSpace(this.content[this.cursor])) {
					++this.cursor;
				}
				return new Token(TokenType.COMMAND, this.content.slice(left, right));
			} else if (this.content[this.cursor] == ':') {
				let left = this.cursor + 1;
				while (this.cursor < this.content.length) {
					if (this.content[this.cursor] == '@' && this.content[this.cursor + 1] == '@') {
						this.cursor += 2;
						break;
					}
					++this.cursor;
				}
				let right = this.cursor;
				if (this.cursor < this.content.length) {
					while (Parser.isWhiteSpace(this.content[this.cursor])) {
						++this.cursor;
					}
				}
				return new Token(TokenType.TEXT_BLOCK, this.content.slice(left, right - 2));
			} else if (this.content[this.cursor] == '{') {
				let left = this.cursor + 1;
				if (this.content[this.cursor + 1] == '}') {
					this.cursor += 2;
					return new Token(TokenType.TEXT, '');
				} else {
					while (this.content[this.cursor] != '}') {
						++this.cursor;
					}
					let right = this.cursor;
					++this.cursor;
					return new Token(TokenType.TEXT, this.content.slice(left, right));
				}
			} else if (this.content[this.cursor] == '(') {
				let left = this.cursor + 1;
				if (this.content[this.cursor + 1] == ')') {
					this.cursor += 2;
					return new Token(TokenType.COMMAND_PARAMETERS, '');
				} else {
					while (this.content[this.cursor] != ')') {
						++this.cursor;
					}
					let right = this.cursor;
					++this.cursor;
					return new Token(TokenType.COMMAND_PARAMETERS, this.content.slice(left, right));
				}
			} else {
				let left = this.cursor;
				while (this.content[this.cursor] && this.content[this.cursor] != '@' && this.content[this.cursor] != '\n') {
					++this.cursor;
				}
				return new Token(TokenType.TEXT, this.content.slice(left, this.cursor));
			}
		}
	}

	private static isAlphaNumeric(char: string) {
		return char && char.match(/[0-9A-Za-z]/) != null;
	}

	private static isWhiteSpace(char: string) {
		return char && char.trim().length == 0;
	}

	private cursor: number = 0;
	private content: string;
}
